This notebook explores the use of SingleR to perform cell-type annotation on datasets from the ScPCA project SCPCP000007 (Gawad lab data).

Note: The first time you run this code it may take a few more minutes due to reference downloads, but they will be cached for faster future execution.

0.1 Set Up

# load the R project
project_root <- here::here()
renv::load(project_root)

suppressPackageStartupMessages({
  library(SingleCellExperiment)
  library(SingleR)
  library(celldex)
  library(ggplot2)
})
theme_set(theme_bw())
set.seed(params$seed) # unclear if this is doing anything? probably not. but maybe later!

utils_dir <- file.path(project_root, "scripts", "utils")
source(file.path(utils_dir, "integration-helpers.R"))

sce_file_suffix <- "processed_citeseq.rds"

integrated_sce_file <- file.path(params$integrated_sce_dir, paste0(
  params$project_id, "_integrated_", params$integration_method, "_sce.rds"
))
if (!file.exists(integrated_sce_file)){
  stop("Integrated SCE file could not be found.")
}

Read in the data:

library_metadata_df <- readr::read_tsv(params$library_file)
Rows: 25 Columns: 16
── Column specification ────────────────────────────────────────────────────────
Delimiter: "\t"
chr (15): project_name, submitter, library_biomaterial_id, sample_biomateria...
lgl  (1): has_CITEseq

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
integrated_sce <- readr::read_rds(integrated_sce_file)

# Define the unintegrated SCE filenames
sce_file_paths <- library_metadata_df %>%
  dplyr::filter(project_name == params$project_id) %>%
  dplyr::mutate(sce_file_path = file.path(
    integration_input_dir, sample_biomaterial_id, glue::glue("{library_biomaterial_id}_{sce_file_suffix}")
  )) %>%
  dplyr::pull(sce_file_path)

0.2 SingleR annotation

Here we performn celltype annotation with the given reference in params$reference on each of the Gawad libraries and look at their UMAPs colored by celltype.

if (params$reference == "hpca") {
  ref_data <- celldex::HumanPrimaryCellAtlasData(ensembl = TRUE)
} else if (params$reference == "blueprint_encode") {
  ref_data <- celldex::BlueprintEncodeData(ensembl = TRUE)
} else {
  stop("Bad reference parameter; either 'hpca' or 'blueprint_encode'.")
}
snapshotDate(): 2022-04-26
see ?celldex and browseVignettes('celldex') for documentation
loading from cache
see ?celldex and browseVignettes('celldex') for documentation
loading from cache
snapshotDate(): 2022-04-25
loading from cache
require("ensembldb")
Warning: Unable to map 1470 of 19363 requested IDs.
annotate_SingleR <- function(sce, ref_data) {
  # return updated sce and the predictions themselves
  
  preds <- SingleR::SingleR(
    test = sce, 
    ref = ref_data,
    labels = ref_data$label.main
  )
  
  # Add `pruned.labels`, where low-confidence annotations are NA, to sce
   sce$SingleR_annotations <- preds$pruned.labels
  
   return(
     list(
       sce = sce,
       preds = preds
       )
   )
}

plot_SingleR <- function(annotation_output, colname) {
  # annotation_output: list of sce and preds
  # colname: column name to plot
  # Make a heatmap and UMAP and print out
  
  heatmap <- SingleR::plotScoreHeatmap(annotation_output[["preds"]], 
                                       # default but let's be explicit
                                       show.pruned = FALSE)

  
  # In case, for `levels()` below.
  colData(annotation_output[["sce"]])[,colname] <- as.factor(colData(annotation_output[["sce"]])[,colname])
  
  umap_df <- tibble::as_tibble(reducedDim(annotation_output[["sce"]], "UMAP")) %>%
    dplyr::select(UMAP1 = V1, UMAP2 = V2) %>%
    dplyr::mutate(celltypes = colData(annotation_output[["sce"]])[,colname])
  
  # We would probably like a more principled approach to colors that is
  #  actually based on biological information about celltypes
  plot_colors <- rainbow( length(levels(umap_df$celltypes)) )
  
  umap <- ggplot(umap_df) + 
    aes(x = UMAP1, y = UMAP2, color = celltypes) +
    geom_point(size = 0.2, alpha = 0.5) + 
    scale_color_manual(values = plot_colors) +
    guides(color = guide_legend(override.aes = list(size=3))) +
    ggtitle(glue::glue("UMAP with top {params$max_celltypes} celltypes shown"))
  
  print(heatmap)
  print(umap)
  
}

# Function to run and plot SingleR
# Return SCE with annotations `SingleR_annotations` column
run_SingleR <- function(sce, 
                        max_celltypes = params$max_celltypes, 
                        viz = TRUE) {
  # Print out the library
  print(unique( metadata(sce)$library ))
  
  # Run annotations
  anno <- annotate_SingleR(sce, ref_data)
  
  # Create an additional column `SingleR_annotations_collapsed` with only max_celltypes
  anno[["sce"]]$SingleR_annotations_collapsed <- forcats::fct_lump_n(
    anno[["sce"]]$SingleR_annotations, 
    max_celltypes
  )
  
  # plot if TRUE
  if (viz) {
    # there is no interesting color palette harmonization among library colors here!
    plot_SingleR(anno, "SingleR_annotations_collapsed")
  }
  return(anno[["sce"]])
}

Here we go!

# Read in all SCE files
sce_list <- purrr::map(
  sce_file_paths, 
  readr::read_rds
)

# Annotate them all, popping out some viz along the way
sce_list_annotated <- purrr::map(sce_list, run_SingleR, viz = TRUE)
[1] "SCPCL000295"
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
Using compatibility `.name_repair`.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

[1] "SCPCL000296"

[1] "SCPCL000297"

[1] "SCPCL000298"

[1] "SCPCL000299"

# for sanity during dev:
readr::write_rds(sce_list_annotated, "sce_list_annotated.rds")

Now let’s plot, to start, the fastMNN UMAP but with celltype annotations from individual libraries. As is deeply commented all over the place, we definitely need to infuse palettes with biology!

# helper to count the number of celltypes across all libraries
count_celltypes <- function(sce) {
 tibble::tibble(celltype = colData(sce)$SingleR_annotations ) %>%
    dplyr::count(celltype) %>%
    dplyr::arrange(-n) %>%
    tidyr::drop_na() 
}

# Celltype order based on **overall** counts for all pooled annotations 
celltype_order <- purrr::map_df(sce_list_annotated, count_celltypes) %>%
  dplyr::group_by(celltype) %>%
  dplyr::summarize(celltype_counts = sum(n)) %>%
  dplyr::arrange(-celltype_counts) %>%
  dplyr::pull(celltype)


# Again we'd eventually like some biology to go into the color choices!
cell_colors <- rainbow(length(celltype_order))



# Find all the annotations for a combined color palette:
# This function is also used later for ADT extraction
extract_annotation <- function(sce, annotation_column_name) {
 tibble::tibble(batch = unique(metadata(sce)$library),
                celltype = colData(sce)[,annotation_column_name], 
                cell_barcode = rownames(colData(sce))) %>%
    dplyr::mutate(cell_name = paste(cell_barcode, batch, sep = "-"))
}


# integrated UMAP with individual library cell annotations colored
purrr::map_df(sce_list_annotated, extract_annotation, "SingleR_annotations") %>%
  dplyr::inner_join(
    tibble::as_tibble(colData(integrated_sce), rownames = "cell_name")
  ) %>%
  dplyr::bind_cols(
    as.data.frame(reducedDim(integrated_sce, paste0(params$integration_method, "_UMAP")))
  ) %>%
  dplyr::mutate(UMAP1 = V1, UMAP2 = V2) %>%
  # And let's make a UMAP!
  ggplot() +
  aes(x = UMAP1, y = UMAP2, color = celltype) + 
  geom_point(size = 0.2, alpha = 0.3) + 
  # we definitely want some biology here!
  scale_color_manual(values = cell_colors)
Joining, by = c("batch", "cell_name")

0.3 ADTs

After annotating, we’d like to validate with ADTs.

find_max_adt <- function(sce) {
  max_adt_indices <- apply(
    logcounts(altExp(sce)),
    2,
    which.max
  )
  # associated rownames which are the ADT names
  max_adt <- rownames(altExp(sce))[max_adt_indices]
  
  # add back to sce and return
  sce$max_adt <- max_adt
  return(sce)
}


sce_list_adt <- purrr::map(sce_list_annotated, find_max_adt)


# What are the unique ADTs for each library?
purrr::map(
  lapply(sce_list_adt, `[[`, "max_adt"), 
  unique
)
[[1]]
 [1] "CD36"    "CD45"    "CD33"    "CD71"    "CD32"    "CD45RA"  "CD123"  
 [8] "CD3"     "CD90"    "CD19"    "CD235ab" "CD10"    "CD7"    

[[2]]
[1] "CD33"   "CD45"   "CD36"   "CD10"   "CD123"  "CD71"   "CD366"  "CD45RA"

[[3]]
 [1] "CD45"   "CD49f"  "CD71"   "CD33"   "CD32"   "CD123"  "CD10"   "CD36"  
 [9] "CD7"    "CD274"  "CD45RA" "CD90"   "CD3"   

[[4]]
 [1] "CD123"  "CD45RA" "CD71"   "CD45"   "CD36"   "CD32"   "CD19"   "CD7"   
 [9] "CD49f"  "CD3"    "CD10"   "CD90"   "CD133"  "CD25"   "CD366"  "CD274" 

[[5]]
 [1] "CD45"    "CD49f"   "CD71"    "CD32"    "CD235ab" "CD90"    "CD45RA" 
 [8] "CD7"     "CD3"     "CD274"   "CD123"   "CD25"    "CD366"   "CD36"   
[15] "CD10"    "CD279"  
CD36

CD45

CD33

CD71

CD32

CD45RA

CD123

CD3

CD90

CD19

CD235ab

CD10

CD7
CD33

CD45

CD36

CD10

CD123

CD71

CD366

CD45RA
CD45

CD49f

CD71

CD33

CD32

CD123

CD10

CD36

CD7

CD274

CD45RA

CD90

CD3
CD123

CD45RA

CD71

CD45

CD36

CD32

CD19

CD7

CD49f

CD3

CD10

CD90

CD133

CD25

CD366

CD274
CD45

CD49f

CD71

CD32

CD235ab

CD90

CD45RA

CD7

CD3

CD274

CD123

CD25

CD366

CD36

CD10

CD279
# What are the ADT distributions for each library?
purrr::map(
  lapply(sce_list_adt, `[[`, "max_adt"), 
  table
)
[[1]]

   CD10   CD123    CD19 CD235ab     CD3    CD32    CD33    CD36    CD45  CD45RA 
      6     366       7      68       5     836    2445     292     626      15 
    CD7    CD71    CD90 
     13      19       2 

[[2]]

  CD10  CD123   CD33   CD36  CD366   CD45 CD45RA   CD71 
     2      9   2845      7      1    289     17      4 

[[3]]

  CD10  CD123  CD274    CD3   CD32   CD33   CD36   CD45 CD45RA  CD49f    CD7 
     6     10      4      1     56      4      1   3289      2    299      2 
  CD71   CD90 
    10      3 

[[4]]

  CD10  CD123  CD133   CD19   CD25  CD274    CD3   CD32   CD36  CD366   CD45 
     1   1747      1     14      4      3     12     17     31      1    726 
CD45RA  CD49f    CD7   CD71   CD90 
  1443     22     76    143     63 

[[5]]

   CD10   CD123 CD235ab    CD25   CD274   CD279     CD3    CD32    CD36   CD366 
      1      18       3       4       9       4      16       5       1       1 
   CD45  CD45RA   CD49f     CD7    CD71    CD90 
    548       1     109      14      17      38 
# What's the most common ADT distributions for each library?
most_common <- function(a_list) {
  sort(table(a_list), decreasing = TRUE)[1] %>%
    names()
}
purrr::map(
  lapply(sce_list_adt, `[[`, "max_adt"), 
  most_common
)
[[1]]
[1] "CD33"

[[2]]
[1] "CD33"

[[3]]
[1] "CD45"

[[4]]
[1] "CD123"

[[5]]
[1] "CD45"
CD33
CD33
CD45
CD123
CD45
  • “CD33…is a transmembrane receptor expressed on cells of myeloid lineage. It is usually considered myeloid-specific, but it can also be found on some lymphoid cells.” source
  • “CD45 is a type I transmembrane protein that is present in various isoforms on all differentiated hematopoietic cells (except erythrocytes and plasma cells).” source
  • “The interleukin-3 receptor (CD123) is a molecule found on cells which helps transmit the signal of interleukin-3, a soluble cytokine important in the immune system….The receptor, found on pluripotent progenitor cells, induces tyrosine phosphorylation within the cell and promotes proliferation and differentiation within the hematopoietic cell lines. It can be found on basophils and pDCs as well as some cDCs among peripheral blood mononuclear cells….CD123 is expressed across acute myeloid leukemia (AML) subtypes, including leukemic stem cells.” source

UMAP of the ADTs:

purrr::map_df(sce_list_adt, extract_annotation, "max_adt") %>%
  dplyr::inner_join(
    tibble::as_tibble(colData(integrated_sce), rownames = "cell_name")
  ) %>%
  dplyr::bind_cols(
    as.data.frame(reducedDim(integrated_sce, paste0(params$integration_method, "_UMAP")))
  ) %>%
  dplyr::mutate(UMAP1 = V1, UMAP2 = V2) %>%
  # And let's make a UMAP!
  ggplot() +
  aes(x = UMAP1, y = UMAP2, color = celltype) + 
  geom_point(size = 0.2, alpha = 0.3)
Joining, by = c("batch", "cell_name")

0.4 Unevaluated initial exploration

Currently the code chunks in this section are not evaluated, but remain here for now for posterity.

To build an initial sense of what we can expect annotating with SingleR, let’s just look at one SCE (arbitrarily chosen as the first):

# For now let's just explore one!
sce_file <- sce_file_paths[[1]]

sce <- readr::read_rds(sce_file)
sce

The celldex package contains bulk RNA-Seq datasets for use as reference. Since this is AML data, we’ll want a reference that has a lot of blood information. According to the celldex, the Human Primary Cell Atlas data will be our closest match.

The HPCA reference consists of publicly available microarray datasets derived from human primary cells (Mabbott et al. 2013). Most of the labels refer to blood subpopulations but cell types from other tissues are also available.

# define reference with ensembl IDs to match our IDs
ref_se <- celldex::HumanPrimaryCellAtlasData(ensembl = TRUE)

# Predict cell types
#  If we change to `labels = ref_se$label.fine`, we'll get more 
#  fine-grained annotations with subtypes etc.
preds_hpca <- SingleR::SingleR(
  # dataset we want to annotate
  test = sce, 
  # reference dataset
  ref = ref_se,
  # label.main is broad 
  labels = ref_se$label.main)


# Results into a tibble:
preds_df <- tibble::as_tibble(preds_hpca$labels) %>%
  dplyr::rename(celltype = value) %>%
  dplyr::mutate(cell_barcode = rownames(preds_hpca),
                reference = "hpca")

# Save the celltypes:
hpca_celltypes <- unique(ref_se$label.main)

And a heatmap version, showing all celltypes. The bar the top shows the final assignment, which are the rows with highest scores.

SingleR::plotScoreHeatmap(preds_hpca)

But can’t hurt to see how this compares to the Blueprint/ENCODE reference:

The Blueprint/ENCODE reference consists of bulk RNA-seq data for pure stroma and immune cells generated by Blueprint (Martens and Stunnenberg 2013) and ENCODE projects (The ENCODE Project Consortium 2012).

# define reference with ensembl IDs to match our IDs
ref_se <- celldex::BlueprintEncodeData(ensembl = TRUE)

# Predict cell types, broadly
preds_blue_enc <- SingleR::SingleR(
  # dataset we want to annotate
  test = sce, 
  # reference dataset
  ref = ref_se,
  # label.main is broad 
  labels = ref_se$label.main)


# Results into a tibble:
preds_df <- preds_df %>%
  dplyr::bind_rows(
    tibble::as_tibble(preds_blue_enc$labels) %>%
    dplyr::rename(celltype = value) %>%
    dplyr::mutate(cell_barcode = rownames(preds_blue_enc),
                  reference = "blueprint_encode")
  )

# Save the celltypes:
blueprint_celltypes <- unique(ref_se$label.main)

# And the heatmap:
SingleR::plotScoreHeatmap(preds_blue_enc)

What did the references predict?

# How many of each celltype?
preds_df %>%
  dplyr::filter(reference == "hpca") %>%
  dplyr::count(celltype) %>%
  dplyr::arrange(-n)


preds_df %>%
  dplyr::filter(reference == "blueprint_encode") %>%
  dplyr::count(celltype) %>%
  dplyr::arrange(-n)

We’ll have to harmonize the celltype names between references to do a robust comparison, but from a very quick glance, overlap is thinner than one might like. That said, we don’t necessarily expect Blueprint/ENCODE to do particularly well anyways!

For a clearer comparison, we’ll harmonize predicted celltypes, but let’s just focus on exactly matching celltypes as a start -

# Let's see what we have here:
sort(hpca_celltypes)
sort(blueprint_celltypes)

# Manually compiled data rame of celltypes roughly present in BOTH references
#  for now leaving the Pre/Pro B-cells out
shared_celltypes_df <- tibble::tribble(
  ~hpca_celltype, ~blueprint_celltype,
  # Both references have it:
  "Astrocyte", "Astrocytes",
  "B_cell", "B-cells",
  "Chondrocytes","Chondrocytes",
  "DC", "DC", 
  "Endothelial_cells", "Endothelial cells", 
  "Epithelial_cells", "Epithelial cells", 
  "Fibroblasts", "Fibroblasts", 
  "Keratinocytes", "Keratinocytes", 
  "Macrophage", "Macrophages", 
  "Monocyte", "Monocytes", 
  "Neurons", "Neurons", 
  "Neutrophils", "Neutrophils", 
  "NK_cell", "NK cells", 
  "Smooth_muscle_cells", "Smooth muscle", 
  # two groups - collapse back to overall T cells
  "T_cells", "CD8+ T-cells",
  "T_cells", "CD4+ T-cells",
  # two groups of HSCs, again, collapse back to overall
  "HSC_-G-CSF", "HSC", 
  "HSC_CD34+", "HSC"
) %>%
  dplyr::mutate(harmonized_celltype = ifelse(blueprint_celltype == "HSC",
                                             blueprint_celltype, 
                                             hpca_celltype))

harmonize_celltypes <- function(preds_df, reference_name) {
  if (reference_name == "hpca") {
    shared_celltypes_colname <- rlang::sym("hpca_celltype")
  } else {
    shared_celltypes_colname <- rlang::sym("blueprint_celltype")
  }
  
  filtered_preds_df <- preds_df %>%
    dplyr::filter(reference == reference_name) 
  
  sym_reference_name <- rlang::sym(reference_name)
  filtered_preds_df %>%
    dplyr::inner_join(
      dplyr::select(
        shared_celltypes_df, 
        celltype = {{shared_celltypes_colname}},
        harmonized_celltype
      )
    ) %>% 
    dplyr::select(cell_barcode, {{sym_reference_name}} := harmonized_celltype) %>%
    dplyr::right_join(filtered_preds_df) %>%
    dplyr::mutate({{reference_name}} := dplyr::if_else(
      {{sym_reference_name}} %in% shared_celltypes_df$harmonized_celltype,
      {{sym_reference_name}},
      celltype
    )) %>% 
    dplyr::select(-celltype, -reference)
  }
  
preds_df_harmonized <- harmonize_celltypes(preds_df, "blueprint_encode") %>%
  dplyr::left_join(
    harmonize_celltypes(preds_df, "hpca")  
  )

# How often do they match?

preds_df_harmonized %>%
  dplyr::summarize(percent_same = sum(blueprint_encode == hpca) / dplyr::n() )


# Where do they not match?
preds_df_harmonized %>%
  dplyr::filter(hpca != blueprint_encode)
  
# Are there specific combinations that get differently annotated?
preds_df_harmonized %>%
  dplyr::filter(hpca != blueprint_encode) %>%
  dplyr::select(-cell_barcode) %>%
  dplyr::count(blueprint_encode, hpca) %>%
  dplyr::arrange(-n)

0.5 Session Info

sessionInfo()
R version 4.2.1 (2022-06-23)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur ... 10.16

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats4    stats     graphics  grDevices datasets  utils     methods  
[8] base     

other attached packages:
 [1] ensembldb_2.20.2            AnnotationFilter_1.20.0    
 [3] GenomicFeatures_1.48.3      AnnotationDbi_1.58.0       
 [5] magrittr_2.0.3              ggplot2_3.3.6              
 [7] celldex_1.6.0               SingleR_1.10.0             
 [9] SingleCellExperiment_1.18.0 SummarizedExperiment_1.26.1
[11] Biobase_2.56.0              GenomicRanges_1.48.0       
[13] GenomeInfoDb_1.32.3         IRanges_2.30.0             
[15] S4Vectors_0.34.0            BiocGenerics_0.42.0        
[17] MatrixGenerics_1.8.1        matrixStats_0.62.0         

loaded via a namespace (and not attached):
  [1] AnnotationHub_3.4.0           BiocFileCache_2.4.0          
  [3] lazyeval_0.2.2                splines_4.2.1                
  [5] BiocParallel_1.30.3           digest_0.6.29                
  [7] htmltools_0.5.3               viridis_0.6.2                
  [9] fansi_1.0.3                   memoise_2.0.1                
 [11] ScaledMatrix_1.4.0            tzdb_0.3.0                   
 [13] Biostrings_2.64.0             miQC_1.4.0                   
 [15] readr_2.1.2                   vroom_1.5.7                  
 [17] prettyunits_1.1.1             colorspace_2.0-3             
 [19] blob_1.2.3                    rappdirs_0.3.3               
 [21] xfun_0.32                     dplyr_1.0.9                  
 [23] crayon_1.5.1                  RCurl_1.98-1.8               
 [25] jsonlite_1.8.0                glue_1.6.2                   
 [27] gtable_0.3.0                  zlibbioc_1.42.0              
 [29] XVector_0.36.0                DelayedArray_0.22.0          
 [31] BiocSingular_1.12.0           scales_1.2.0                 
 [33] pheatmap_1.0.12               DBI_1.1.3                    
 [35] Rcpp_1.0.9                    viridisLite_0.4.0            
 [37] xtable_1.8-4                  progress_1.2.2               
 [39] bit_4.0.4                     rsvd_1.0.5                   
 [41] httr_1.4.3                    RColorBrewer_1.1-3           
 [43] modeltools_0.2-23             ellipsis_0.3.2               
 [45] pkgconfig_2.0.3               XML_3.99-0.10                
 [47] flexmix_2.3-18                farver_2.1.1                 
 [49] nnet_7.3-17                   sass_0.4.2                   
 [51] dbplyr_2.2.1                  utf8_1.2.2                   
 [53] here_1.0.1                    tidyselect_1.1.2             
 [55] labeling_0.4.2                rlang_1.0.4                  
 [57] later_1.3.0                   munsell_0.5.0                
 [59] BiocVersion_3.15.2            tools_4.2.1                  
 [61] cachem_1.0.6                  cli_3.3.0                    
 [63] generics_0.1.3                RSQLite_2.2.15               
 [65] ExperimentHub_2.4.0           evaluate_0.16                
 [67] stringr_1.4.0                 fastmap_1.1.0                
 [69] yaml_2.3.5                    knitr_1.39                   
 [71] bit64_4.0.5                   purrr_0.3.4                  
 [73] KEGGREST_1.36.3               sparseMatrixStats_1.8.0      
 [75] mime_0.12                     xml2_1.3.3                   
 [77] biomaRt_2.52.0                compiler_4.2.1               
 [79] filelock_1.0.2                curl_4.3.2                   
 [81] png_0.1-7                     interactiveDisplayBase_1.34.0
 [83] tibble_3.1.8                  bslib_0.4.0                  
 [85] stringi_1.7.8                 highr_0.9                    
 [87] forcats_0.5.2                 lattice_0.20-45              
 [89] ProtGenerics_1.28.0           Matrix_1.4-1                 
 [91] vctrs_0.4.1                   pillar_1.8.0                 
 [93] lifecycle_1.0.1               BiocManager_1.30.18          
 [95] jquerylib_0.1.4               BiocNeighbors_1.14.0         
 [97] bitops_1.0-7                  irlba_2.3.5                  
 [99] httpuv_1.6.5                  rtracklayer_1.56.1           
[101] R6_2.5.1                      BiocIO_1.6.0                 
[103] promises_1.2.0.1              renv_0.15.5                  
[105] gridExtra_2.3                 codetools_0.2-18             
[107] assertthat_0.2.1              rprojroot_2.0.3              
[109] rjson_0.2.21                  withr_2.5.0                  
[111] GenomicAlignments_1.32.1      Rsamtools_2.12.0             
[113] GenomeInfoDbData_1.2.8        parallel_4.2.1               
[115] hms_1.1.1                     grid_4.2.1                   
[117] beachmat_2.12.0               tidyr_1.2.0                  
[119] rmarkdown_2.14                DelayedMatrixStats_1.18.0    
[121] shiny_1.7.2                   restfulr_0.0.15              
LS0tCnBhcmFtczoKICBzZWVkOiAyMDIyCiAgbGlicmFyeV9maWxlOiAhciBmaWxlLnBhdGgoInNhbXBsZS1pbmZvIiwgInNjcGNhLXByb2Nlc3NlZC1saWJyYXJpZXMudHN2IikKICBwcm9qZWN0X2lkOiAiU0NQQ1AwMDAwMDciCiAgcmVmZXJlbmNlOiAiaHBjYSIKICBpbnRlZ3JhdGVkX3NjZV9kaXI6ICFyIGZpbGUucGF0aCgicmVzdWx0cyIsICJzY3BjYSIsICJpbnRlZ3JhdGVkX3NjZSIpCiAgaW50ZWdyYXRpb25fbWV0aG9kOiAiZmFzdG1ubiIKICBtYXhfY2VsbHR5cGVzOiA1CnRpdGxlOiAiQ2VsbCB0eXBlIGFubm90YXRpb24gZXhwbG9yYXRpb24iCmF1dGhvcjogIkRhdGEgTGFiIgpkYXRlOiAiYHIgcGFyYW1zJGRhdGVgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KClRoaXMgbm90ZWJvb2sgZXhwbG9yZXMgdGhlIHVzZSBvZiBbYFNpbmdsZVJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvcmVsZWFzZS9TaW5nbGVSQm9vay8pIHRvIHBlcmZvcm0gY2VsbC10eXBlIGFubm90YXRpb24gb24gZGF0YXNldHMgZnJvbSB0aGUgU2NQQ0EgcHJvamVjdCBTQ1BDUDAwMDAwNyAoR2F3YWQgbGFiIGRhdGEpLgoKKipOb3RlOioqIFRoZSBmaXJzdCB0aW1lIHlvdSBydW4gdGhpcyBjb2RlIGl0IG1heSB0YWtlIGEgZmV3IG1vcmUgbWludXRlcyBkdWUgdG8gcmVmZXJlbmNlIGRvd25sb2FkcywgYnV0IHRoZXkgd2lsbCBiZSBjYWNoZWQgZm9yIGZhc3RlciBmdXR1cmUgZXhlY3V0aW9uLgoKIyMgU2V0IFVwCgpgYGB7ciBzZXR1cH0KIyBsb2FkIHRoZSBSIHByb2plY3QKcHJvamVjdF9yb290IDwtIGhlcmU6OmhlcmUoKQpyZW52Ojpsb2FkKHByb2plY3Rfcm9vdCkKCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKICBsaWJyYXJ5KFNpbmdsZVIpCiAgbGlicmFyeShjZWxsZGV4KQogIGxpYnJhcnkoZ2dwbG90MikKfSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCnNldC5zZWVkKHBhcmFtcyRzZWVkKSAjIHVuY2xlYXIgaWYgdGhpcyBpcyBkb2luZyBhbnl0aGluZz8gcHJvYmFibHkgbm90LiBidXQgbWF5YmUgbGF0ZXIhCgp1dGlsc19kaXIgPC0gZmlsZS5wYXRoKHByb2plY3Rfcm9vdCwgInNjcmlwdHMiLCAidXRpbHMiKQpzb3VyY2UoZmlsZS5wYXRoKHV0aWxzX2RpciwgImludGVncmF0aW9uLWhlbHBlcnMuUiIpKQoKc2NlX2ZpbGVfc3VmZml4IDwtICJwcm9jZXNzZWRfY2l0ZXNlcS5yZHMiCgppbnRlZ3JhdGVkX3NjZV9maWxlIDwtIGZpbGUucGF0aChwYXJhbXMkaW50ZWdyYXRlZF9zY2VfZGlyLCBwYXN0ZTAoCiAgcGFyYW1zJHByb2plY3RfaWQsICJfaW50ZWdyYXRlZF8iLCBwYXJhbXMkaW50ZWdyYXRpb25fbWV0aG9kLCAiX3NjZS5yZHMiCikpCmlmICghZmlsZS5leGlzdHMoaW50ZWdyYXRlZF9zY2VfZmlsZSkpewogIHN0b3AoIkludGVncmF0ZWQgU0NFIGZpbGUgY291bGQgbm90IGJlIGZvdW5kLiIpCn0KYGBgCgpSZWFkIGluIHRoZSBkYXRhOgoKYGBge3J9CmxpYnJhcnlfbWV0YWRhdGFfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHBhcmFtcyRsaWJyYXJ5X2ZpbGUpCgppbnRlZ3JhdGVkX3NjZSA8LSByZWFkcjo6cmVhZF9yZHMoaW50ZWdyYXRlZF9zY2VfZmlsZSkKCiMgRGVmaW5lIHRoZSB1bmludGVncmF0ZWQgU0NFIGZpbGVuYW1lcwpzY2VfZmlsZV9wYXRocyA8LSBsaWJyYXJ5X21ldGFkYXRhX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIocHJvamVjdF9uYW1lID09IHBhcmFtcyRwcm9qZWN0X2lkKSAlPiUKICBkcGx5cjo6bXV0YXRlKHNjZV9maWxlX3BhdGggPSBmaWxlLnBhdGgoCiAgICBpbnRlZ3JhdGlvbl9pbnB1dF9kaXIsIHNhbXBsZV9iaW9tYXRlcmlhbF9pZCwgZ2x1ZTo6Z2x1ZSgie2xpYnJhcnlfYmlvbWF0ZXJpYWxfaWR9X3tzY2VfZmlsZV9zdWZmaXh9IikKICApKSAlPiUKICBkcGx5cjo6cHVsbChzY2VfZmlsZV9wYXRoKQoKCmBgYAoKCiMjIGBTaW5nbGVSYCBhbm5vdGF0aW9uCgpIZXJlIHdlIHBlcmZvcm1uIGNlbGx0eXBlIGFubm90YXRpb24gd2l0aCB0aGUgZ2l2ZW4gcmVmZXJlbmNlIGluIGBwYXJhbXMkcmVmZXJlbmNlYCBvbiBlYWNoIG9mIHRoZSBHYXdhZCBsaWJyYXJpZXMgYW5kIGxvb2sgYXQgdGhlaXIgVU1BUHMgY29sb3JlZCBieSBjZWxsdHlwZS4KCmBgYHtyfQppZiAocGFyYW1zJHJlZmVyZW5jZSA9PSAiaHBjYSIpIHsKICByZWZfZGF0YSA8LSBjZWxsZGV4OjpIdW1hblByaW1hcnlDZWxsQXRsYXNEYXRhKGVuc2VtYmwgPSBUUlVFKQp9IGVsc2UgaWYgKHBhcmFtcyRyZWZlcmVuY2UgPT0gImJsdWVwcmludF9lbmNvZGUiKSB7CiAgcmVmX2RhdGEgPC0gY2VsbGRleDo6Qmx1ZXByaW50RW5jb2RlRGF0YShlbnNlbWJsID0gVFJVRSkKfSBlbHNlIHsKICBzdG9wKCJCYWQgcmVmZXJlbmNlIHBhcmFtZXRlcjsgZWl0aGVyICdocGNhJyBvciAnYmx1ZXByaW50X2VuY29kZScuIikKfQoKCmFubm90YXRlX1NpbmdsZVIgPC0gZnVuY3Rpb24oc2NlLCByZWZfZGF0YSkgewogICMgcmV0dXJuIHVwZGF0ZWQgc2NlIGFuZCB0aGUgcHJlZGljdGlvbnMgdGhlbXNlbHZlcwogIAogIHByZWRzIDwtIFNpbmdsZVI6OlNpbmdsZVIoCiAgICB0ZXN0ID0gc2NlLCAKICAgIHJlZiA9IHJlZl9kYXRhLAogICAgbGFiZWxzID0gcmVmX2RhdGEkbGFiZWwubWFpbgogICkKICAKICAjIEFkZCBgcHJ1bmVkLmxhYmVsc2AsIHdoZXJlIGxvdy1jb25maWRlbmNlIGFubm90YXRpb25zIGFyZSBOQSwgdG8gc2NlCiAgIHNjZSRTaW5nbGVSX2Fubm90YXRpb25zIDwtIHByZWRzJHBydW5lZC5sYWJlbHMKICAKICAgcmV0dXJuKAogICAgIGxpc3QoCiAgICAgICBzY2UgPSBzY2UsCiAgICAgICBwcmVkcyA9IHByZWRzCiAgICAgICApCiAgICkKfQoKcGxvdF9TaW5nbGVSIDwtIGZ1bmN0aW9uKGFubm90YXRpb25fb3V0cHV0LCBjb2xuYW1lKSB7CiAgIyBhbm5vdGF0aW9uX291dHB1dDogbGlzdCBvZiBzY2UgYW5kIHByZWRzCiAgIyBjb2xuYW1lOiBjb2x1bW4gbmFtZSB0byBwbG90CiAgIyBNYWtlIGEgaGVhdG1hcCBhbmQgVU1BUCBhbmQgcHJpbnQgb3V0CiAgCiAgaGVhdG1hcCA8LSBTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKGFubm90YXRpb25fb3V0cHV0W1sicHJlZHMiXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRlZmF1bHQgYnV0IGxldCdzIGJlIGV4cGxpY2l0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3cucHJ1bmVkID0gRkFMU0UpCgogIAogICMgSW4gY2FzZSwgZm9yIGBsZXZlbHMoKWAgYmVsb3cuCiAgY29sRGF0YShhbm5vdGF0aW9uX291dHB1dFtbInNjZSJdXSlbLGNvbG5hbWVdIDwtIGFzLmZhY3Rvcihjb2xEYXRhKGFubm90YXRpb25fb3V0cHV0W1sic2NlIl1dKVssY29sbmFtZV0pCiAgCiAgdW1hcF9kZiA8LSB0aWJibGU6OmFzX3RpYmJsZShyZWR1Y2VkRGltKGFubm90YXRpb25fb3V0cHV0W1sic2NlIl1dLCAiVU1BUCIpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoVU1BUDEgPSBWMSwgVU1BUDIgPSBWMikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGx0eXBlcyA9IGNvbERhdGEoYW5ub3RhdGlvbl9vdXRwdXRbWyJzY2UiXV0pWyxjb2xuYW1lXSkKICAKICAjIFdlIHdvdWxkIHByb2JhYmx5IGxpa2UgYSBtb3JlIHByaW5jaXBsZWQgYXBwcm9hY2ggdG8gY29sb3JzIHRoYXQgaXMKICAjICBhY3R1YWxseSBiYXNlZCBvbiBiaW9sb2dpY2FsIGluZm9ybWF0aW9uIGFib3V0IGNlbGx0eXBlcwogIHBsb3RfY29sb3JzIDwtIHJhaW5ib3coIGxlbmd0aChsZXZlbHModW1hcF9kZiRjZWxsdHlwZXMpKSApCiAgCiAgdW1hcCA8LSBnZ3Bsb3QodW1hcF9kZikgKyAKICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBjZWxsdHlwZXMpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuMiwgYWxwaGEgPSAwLjUpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGxvdF9jb2xvcnMpICsKICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MykpKSArCiAgICBnZ3RpdGxlKGdsdWU6OmdsdWUoIlVNQVAgd2l0aCB0b3Age3BhcmFtcyRtYXhfY2VsbHR5cGVzfSBjZWxsdHlwZXMgc2hvd24iKSkKICAKICBwcmludChoZWF0bWFwKQogIHByaW50KHVtYXApCiAgCn0KCiMgRnVuY3Rpb24gdG8gcnVuIGFuZCBwbG90IFNpbmdsZVIKIyBSZXR1cm4gU0NFIHdpdGggYW5ub3RhdGlvbnMgYFNpbmdsZVJfYW5ub3RhdGlvbnNgIGNvbHVtbgpydW5fU2luZ2xlUiA8LSBmdW5jdGlvbihzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICBtYXhfY2VsbHR5cGVzID0gcGFyYW1zJG1heF9jZWxsdHlwZXMsIAogICAgICAgICAgICAgICAgICAgICAgICB2aXogPSBUUlVFKSB7CiAgIyBQcmludCBvdXQgdGhlIGxpYnJhcnkKICBwcmludCh1bmlxdWUoIG1ldGFkYXRhKHNjZSkkbGlicmFyeSApKQogIAogICMgUnVuIGFubm90YXRpb25zCiAgYW5ubyA8LSBhbm5vdGF0ZV9TaW5nbGVSKHNjZSwgcmVmX2RhdGEpCiAgCiAgIyBDcmVhdGUgYW4gYWRkaXRpb25hbCBjb2x1bW4gYFNpbmdsZVJfYW5ub3RhdGlvbnNfY29sbGFwc2VkYCB3aXRoIG9ubHkgbWF4X2NlbGx0eXBlcwogIGFubm9bWyJzY2UiXV0kU2luZ2xlUl9hbm5vdGF0aW9uc19jb2xsYXBzZWQgPC0gZm9yY2F0czo6ZmN0X2x1bXBfbigKICAgIGFubm9bWyJzY2UiXV0kU2luZ2xlUl9hbm5vdGF0aW9ucywgCiAgICBtYXhfY2VsbHR5cGVzCiAgKQogIAogICMgcGxvdCBpZiBUUlVFCiAgaWYgKHZpeikgewogICAgIyB0aGVyZSBpcyBubyBpbnRlcmVzdGluZyBjb2xvciBwYWxldHRlIGhhcm1vbml6YXRpb24gYW1vbmcgbGlicmFyeSBjb2xvcnMgaGVyZSEKICAgIHBsb3RfU2luZ2xlUihhbm5vLCAiU2luZ2xlUl9hbm5vdGF0aW9uc19jb2xsYXBzZWQiKQogIH0KICByZXR1cm4oYW5ub1tbInNjZSJdXSkKfQpgYGAKCkhlcmUgd2UgZ28hCgpgYGB7cn0KIyBSZWFkIGluIGFsbCBTQ0UgZmlsZXMKc2NlX2xpc3QgPC0gcHVycnI6Om1hcCgKICBzY2VfZmlsZV9wYXRocywgCiAgcmVhZHI6OnJlYWRfcmRzCikKCiMgQW5ub3RhdGUgdGhlbSBhbGwsIHBvcHBpbmcgb3V0IHNvbWUgdml6IGFsb25nIHRoZSB3YXkKc2NlX2xpc3RfYW5ub3RhdGVkIDwtIHB1cnJyOjptYXAoc2NlX2xpc3QsIHJ1bl9TaW5nbGVSLCB2aXogPSBUUlVFKQoKIyBmb3Igc2FuaXR5IGR1cmluZyBkZXY6CnJlYWRyOjp3cml0ZV9yZHMoc2NlX2xpc3RfYW5ub3RhdGVkLCAic2NlX2xpc3RfYW5ub3RhdGVkLnJkcyIpCmBgYAoKCgpOb3cgbGV0J3MgcGxvdCwgdG8gc3RhcnQsIHRoZSBmYXN0TU5OIFVNQVAgYnV0IHdpdGggY2VsbHR5cGUgYW5ub3RhdGlvbnMgZnJvbSBpbmRpdmlkdWFsIGxpYnJhcmllcy4KQXMgaXMgZGVlcGx5IGNvbW1lbnRlZCBhbGwgb3ZlciB0aGUgcGxhY2UsIHdlIGRlZmluaXRlbHkgbmVlZCB0byBpbmZ1c2UgcGFsZXR0ZXMgd2l0aCBiaW9sb2d5IQoKYGBge3J9CiMgaGVscGVyIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgY2VsbHR5cGVzIGFjcm9zcyBhbGwgbGlicmFyaWVzCmNvdW50X2NlbGx0eXBlcyA8LSBmdW5jdGlvbihzY2UpIHsKIHRpYmJsZTo6dGliYmxlKGNlbGx0eXBlID0gY29sRGF0YShzY2UpJFNpbmdsZVJfYW5ub3RhdGlvbnMgKSAlPiUKICAgIGRwbHlyOjpjb3VudChjZWxsdHlwZSkgJT4lCiAgICBkcGx5cjo6YXJyYW5nZSgtbikgJT4lCiAgICB0aWR5cjo6ZHJvcF9uYSgpIAp9CgojIENlbGx0eXBlIG9yZGVyIGJhc2VkIG9uICoqb3ZlcmFsbCoqIGNvdW50cyBmb3IgYWxsIHBvb2xlZCBhbm5vdGF0aW9ucyAKY2VsbHR5cGVfb3JkZXIgPC0gcHVycnI6Om1hcF9kZihzY2VfbGlzdF9hbm5vdGF0ZWQsIGNvdW50X2NlbGx0eXBlcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGNlbGx0eXBlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKGNlbGx0eXBlX2NvdW50cyA9IHN1bShuKSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLWNlbGx0eXBlX2NvdW50cykgJT4lCiAgZHBseXI6OnB1bGwoY2VsbHR5cGUpCgoKIyBBZ2FpbiB3ZSdkIGV2ZW50dWFsbHkgbGlrZSBzb21lIGJpb2xvZ3kgdG8gZ28gaW50byB0aGUgY29sb3IgY2hvaWNlcyEKY2VsbF9jb2xvcnMgPC0gcmFpbmJvdyhsZW5ndGgoY2VsbHR5cGVfb3JkZXIpKQoKCgojIEZpbmQgYWxsIHRoZSBhbm5vdGF0aW9ucyBmb3IgYSBjb21iaW5lZCBjb2xvciBwYWxldHRlOgojIFRoaXMgZnVuY3Rpb24gaXMgYWxzbyB1c2VkIGxhdGVyIGZvciBBRFQgZXh0cmFjdGlvbgpleHRyYWN0X2Fubm90YXRpb24gPC0gZnVuY3Rpb24oc2NlLCBhbm5vdGF0aW9uX2NvbHVtbl9uYW1lKSB7CiB0aWJibGU6OnRpYmJsZShiYXRjaCA9IHVuaXF1ZShtZXRhZGF0YShzY2UpJGxpYnJhcnkpLAogICAgICAgICAgICAgICAgY2VsbHR5cGUgPSBjb2xEYXRhKHNjZSlbLGFubm90YXRpb25fY29sdW1uX25hbWVdLCAKICAgICAgICAgICAgICAgIGNlbGxfYmFyY29kZSA9IHJvd25hbWVzKGNvbERhdGEoc2NlKSkpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX25hbWUgPSBwYXN0ZShjZWxsX2JhcmNvZGUsIGJhdGNoLCBzZXAgPSAiLSIpKQp9CgoKIyBpbnRlZ3JhdGVkIFVNQVAgd2l0aCBpbmRpdmlkdWFsIGxpYnJhcnkgY2VsbCBhbm5vdGF0aW9ucyBjb2xvcmVkCnB1cnJyOjptYXBfZGYoc2NlX2xpc3RfYW5ub3RhdGVkLCBleHRyYWN0X2Fubm90YXRpb24sICJTaW5nbGVSX2Fubm90YXRpb25zIikgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4oCiAgICB0aWJibGU6OmFzX3RpYmJsZShjb2xEYXRhKGludGVncmF0ZWRfc2NlKSwgcm93bmFtZXMgPSAiY2VsbF9uYW1lIikKICApICU+JQogIGRwbHlyOjpiaW5kX2NvbHMoCiAgICBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oaW50ZWdyYXRlZF9zY2UsIHBhc3RlMChwYXJhbXMkaW50ZWdyYXRpb25fbWV0aG9kLCAiX1VNQVAiKSkpCiAgKSAlPiUKICBkcGx5cjo6bXV0YXRlKFVNQVAxID0gVjEsIFVNQVAyID0gVjIpICU+JQogICMgQW5kIGxldCdzIG1ha2UgYSBVTUFQIQogIGdncGxvdCgpICsKICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gY2VsbHR5cGUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC4yLCBhbHBoYSA9IDAuMykgKyAKICAjIHdlIGRlZmluaXRlbHkgd2FudCBzb21lIGJpb2xvZ3kgaGVyZSEKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2VsbF9jb2xvcnMpCmBgYAoKCgojIyBBRFRzCgpBZnRlciBhbm5vdGF0aW5nLCB3ZSdkIGxpa2UgdG8gdmFsaWRhdGUgd2l0aCBBRFRzLgoKYGBge3J9CmZpbmRfbWF4X2FkdCA8LSBmdW5jdGlvbihzY2UpIHsKICBtYXhfYWR0X2luZGljZXMgPC0gYXBwbHkoCiAgICBsb2djb3VudHMoYWx0RXhwKHNjZSkpLAogICAgMiwKICAgIHdoaWNoLm1heAogICkKICAjIGFzc29jaWF0ZWQgcm93bmFtZXMgd2hpY2ggYXJlIHRoZSBBRFQgbmFtZXMKICBtYXhfYWR0IDwtIHJvd25hbWVzKGFsdEV4cChzY2UpKVttYXhfYWR0X2luZGljZXNdCiAgCiAgIyBhZGQgYmFjayB0byBzY2UgYW5kIHJldHVybgogIHNjZSRtYXhfYWR0IDwtIG1heF9hZHQKICByZXR1cm4oc2NlKQp9CgoKc2NlX2xpc3RfYWR0IDwtIHB1cnJyOjptYXAoc2NlX2xpc3RfYW5ub3RhdGVkLCBmaW5kX21heF9hZHQpCgoKIyBXaGF0IGFyZSB0aGUgdW5pcXVlIEFEVHMgZm9yIGVhY2ggbGlicmFyeT8KcHVycnI6Om1hcCgKICBsYXBwbHkoc2NlX2xpc3RfYWR0LCBgW1tgLCAibWF4X2FkdCIpLCAKICB1bmlxdWUKKQoKIyBXaGF0IGFyZSB0aGUgQURUIGRpc3RyaWJ1dGlvbnMgZm9yIGVhY2ggbGlicmFyeT8KcHVycnI6Om1hcCgKICBsYXBwbHkoc2NlX2xpc3RfYWR0LCBgW1tgLCAibWF4X2FkdCIpLCAKICB0YWJsZQopCgojIFdoYXQncyB0aGUgbW9zdCBjb21tb24gQURUIGRpc3RyaWJ1dGlvbnMgZm9yIGVhY2ggbGlicmFyeT8KbW9zdF9jb21tb24gPC0gZnVuY3Rpb24oYV9saXN0KSB7CiAgc29ydCh0YWJsZShhX2xpc3QpLCBkZWNyZWFzaW5nID0gVFJVRSlbMV0gJT4lCiAgICBuYW1lcygpCn0KcHVycnI6Om1hcCgKICBsYXBwbHkoc2NlX2xpc3RfYWR0LCBgW1tgLCAibWF4X2FkdCIpLCAKICBtb3N0X2NvbW1vbgopCgpgYGAKCi0gIkNEMzMuLi5pcyBhIHRyYW5zbWVtYnJhbmUgcmVjZXB0b3IgZXhwcmVzc2VkIG9uIGNlbGxzIG9mIG15ZWxvaWQgbGluZWFnZS4gSXQgaXMgdXN1YWxseSBjb25zaWRlcmVkIG15ZWxvaWQtc3BlY2lmaWMsIGJ1dCBpdCBjYW4gYWxzbyBiZSBmb3VuZCBvbiBzb21lIGx5bXBob2lkIGNlbGxzLiIgW3NvdXJjZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ0QzMykKLSAiQ0Q0NSBpcyBhIHR5cGUgSSB0cmFuc21lbWJyYW5lIHByb3RlaW4gdGhhdCBpcyBwcmVzZW50IGluIHZhcmlvdXMgaXNvZm9ybXMgb24gYWxsIGRpZmZlcmVudGlhdGVkIGhlbWF0b3BvaWV0aWMgY2VsbHMgKGV4Y2VwdCBlcnl0aHJvY3l0ZXMgYW5kIHBsYXNtYSBjZWxscykuIiBbc291cmNlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DRDQ1KQotICJUaGUgaW50ZXJsZXVraW4tMyByZWNlcHRvciAoQ0QxMjMpIGlzIGEgbW9sZWN1bGUgZm91bmQgb24gY2VsbHMgd2hpY2ggaGVscHMgdHJhbnNtaXQgdGhlIHNpZ25hbCBvZiBpbnRlcmxldWtpbi0zLCBhIHNvbHVibGUgY3l0b2tpbmUgaW1wb3J0YW50IGluIHRoZSBpbW11bmUgc3lzdGVtLi4uLlRoZSByZWNlcHRvciwgZm91bmQgb24gcGx1cmlwb3RlbnQgcHJvZ2VuaXRvciBjZWxscywgaW5kdWNlcyB0eXJvc2luZSBwaG9zcGhvcnlsYXRpb24gd2l0aGluIHRoZSBjZWxsIGFuZCBwcm9tb3RlcyBwcm9saWZlcmF0aW9uIGFuZCBkaWZmZXJlbnRpYXRpb24gd2l0aGluIHRoZSBoZW1hdG9wb2lldGljIGNlbGwgbGluZXMuIEl0IGNhbiBiZSBmb3VuZCBvbiBiYXNvcGhpbHMgYW5kIHBEQ3MgYXMgd2VsbCBhcyBzb21lIGNEQ3MgYW1vbmcgcGVyaXBoZXJhbCBibG9vZCBtb25vbnVjbGVhciBjZWxscy4uLi5DRDEyMyBpcyBleHByZXNzZWQgYWNyb3NzIGFjdXRlIG15ZWxvaWQgbGV1a2VtaWEgKEFNTCkgc3VidHlwZXMsIGluY2x1ZGluZyBsZXVrZW1pYyBzdGVtIGNlbGxzLiIgW3NvdXJjZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSW50ZXJsZXVraW4tM19yZWNlcHRvcikKCgpVTUFQIG9mIHRoZSBBRFRzOgoKYGBge3J9CnB1cnJyOjptYXBfZGYoc2NlX2xpc3RfYWR0LCBleHRyYWN0X2Fubm90YXRpb24sICJtYXhfYWR0IikgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4oCiAgICB0aWJibGU6OmFzX3RpYmJsZShjb2xEYXRhKGludGVncmF0ZWRfc2NlKSwgcm93bmFtZXMgPSAiY2VsbF9uYW1lIikKICApICU+JQogIGRwbHlyOjpiaW5kX2NvbHMoCiAgICBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oaW50ZWdyYXRlZF9zY2UsIHBhc3RlMChwYXJhbXMkaW50ZWdyYXRpb25fbWV0aG9kLCAiX1VNQVAiKSkpCiAgKSAlPiUKICBkcGx5cjo6bXV0YXRlKFVNQVAxID0gVjEsIFVNQVAyID0gVjIpICU+JQogICMgQW5kIGxldCdzIG1ha2UgYSBVTUFQIQogIGdncGxvdCgpICsKICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gY2VsbHR5cGUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC4yLCBhbHBoYSA9IDAuMykKYGBgCgoKCgojIyBVbmV2YWx1YXRlZCBpbml0aWFsIGV4cGxvcmF0aW9uCgo+IEN1cnJlbnRseSB0aGUgY29kZSBjaHVua3MgaW4gdGhpcyBzZWN0aW9uIGFyZSBub3QgZXZhbHVhdGVkLCBidXQgcmVtYWluIGhlcmUgZm9yIG5vdyBmb3IgcG9zdGVyaXR5LgoKVG8gYnVpbGQgYW4gaW5pdGlhbCBzZW5zZSBvZiB3aGF0IHdlIGNhbiBleHBlY3QgYW5ub3RhdGluZyB3aXRoIGBTaW5nbGVSYCwgbGV0J3MganVzdCBsb29rIGF0IG9uZSBTQ0UgKGFyYml0cmFyaWx5IGNob3NlbiBhcyB0aGUgZmlyc3QpOgoKYGBge3IsIGV2YWw9Rn0KIyBGb3Igbm93IGxldCdzIGp1c3QgZXhwbG9yZSBvbmUhCnNjZV9maWxlIDwtIHNjZV9maWxlX3BhdGhzW1sxXV0KCnNjZSA8LSByZWFkcjo6cmVhZF9yZHMoc2NlX2ZpbGUpCnNjZQpgYGAKCgpUaGUgYGNlbGxkZXhgIHBhY2thZ2UgY29udGFpbnMgYnVsayBSTkEtU2VxIGRhdGFzZXRzIGZvciB1c2UgYXMgcmVmZXJlbmNlLgpTaW5jZSB0aGlzIGlzIEFNTCBkYXRhLCB3ZSdsbCB3YW50IGEgcmVmZXJlbmNlIHRoYXQgaGFzIGEgbG90IG9mIGJsb29kIGluZm9ybWF0aW9uLgpBY2NvcmRpbmcgdG8gdGhlIFtgY2VsbGRleGBdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2RhdGEvZXhwZXJpbWVudC92aWduZXR0ZXMvY2VsbGRleC9pbnN0L2RvYy91c2VyZ3VpZGUuaHRtbCksIHRoZSBgSHVtYW4gUHJpbWFyeSBDZWxsIEF0bGFzYCBkYXRhIHdpbGwgYmUgb3VyIGNsb3Nlc3QgbWF0Y2guCgo+IFRoZSBIUENBIHJlZmVyZW5jZSBjb25zaXN0cyBvZiBwdWJsaWNseSBhdmFpbGFibGUgbWljcm9hcnJheSBkYXRhc2V0cyBkZXJpdmVkIGZyb20gaHVtYW4gcHJpbWFyeSBjZWxscyAoTWFiYm90dCBldCBhbC4gMjAxMykuIE1vc3Qgb2YgdGhlIGxhYmVscyByZWZlciB0byBibG9vZCBzdWJwb3B1bGF0aW9ucyBidXQgY2VsbCB0eXBlcyBmcm9tIG90aGVyIHRpc3N1ZXMgYXJlIGFsc28gYXZhaWxhYmxlLgoKCmBgYHtyLCBldmFsPUZ9CiMgZGVmaW5lIHJlZmVyZW5jZSB3aXRoIGVuc2VtYmwgSURzIHRvIG1hdGNoIG91ciBJRHMKcmVmX3NlIDwtIGNlbGxkZXg6Okh1bWFuUHJpbWFyeUNlbGxBdGxhc0RhdGEoZW5zZW1ibCA9IFRSVUUpCgojIFByZWRpY3QgY2VsbCB0eXBlcwojICBJZiB3ZSBjaGFuZ2UgdG8gYGxhYmVscyA9IHJlZl9zZSRsYWJlbC5maW5lYCwgd2UnbGwgZ2V0IG1vcmUgCiMgIGZpbmUtZ3JhaW5lZCBhbm5vdGF0aW9ucyB3aXRoIHN1YnR5cGVzIGV0Yy4KcHJlZHNfaHBjYSA8LSBTaW5nbGVSOjpTaW5nbGVSKAogICMgZGF0YXNldCB3ZSB3YW50IHRvIGFubm90YXRlCiAgdGVzdCA9IHNjZSwgCiAgIyByZWZlcmVuY2UgZGF0YXNldAogIHJlZiA9IHJlZl9zZSwKICAjIGxhYmVsLm1haW4gaXMgYnJvYWQgCiAgbGFiZWxzID0gcmVmX3NlJGxhYmVsLm1haW4pCgoKIyBSZXN1bHRzIGludG8gYSB0aWJibGU6CnByZWRzX2RmIDwtIHRpYmJsZTo6YXNfdGliYmxlKHByZWRzX2hwY2EkbGFiZWxzKSAlPiUKICBkcGx5cjo6cmVuYW1lKGNlbGx0eXBlID0gdmFsdWUpICU+JQogIGRwbHlyOjptdXRhdGUoY2VsbF9iYXJjb2RlID0gcm93bmFtZXMocHJlZHNfaHBjYSksCiAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSAiaHBjYSIpCgojIFNhdmUgdGhlIGNlbGx0eXBlczoKaHBjYV9jZWxsdHlwZXMgPC0gdW5pcXVlKHJlZl9zZSRsYWJlbC5tYWluKQpgYGAKCkFuZCBhIGhlYXRtYXAgdmVyc2lvbiwgc2hvd2luZyBhbGwgY2VsbHR5cGVzLiAKVGhlIGJhciB0aGUgdG9wIHNob3dzIHRoZSBmaW5hbCBhc3NpZ25tZW50LCB3aGljaCBhcmUgdGhlIHJvd3Mgd2l0aCBoaWdoZXN0IHNjb3Jlcy4KCmBgYHtyLCBldmFsPUYsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKHByZWRzX2hwY2EpCmBgYAoKQnV0IGNhbid0IGh1cnQgdG8gc2VlIGhvdyB0aGlzIGNvbXBhcmVzIHRvIHRoZSBgQmx1ZXByaW50L0VOQ09ERWAgcmVmZXJlbmNlOgoKPiBUaGUgQmx1ZXByaW50L0VOQ09ERSByZWZlcmVuY2UgY29uc2lzdHMgb2YgYnVsayBSTkEtc2VxIGRhdGEgZm9yIHB1cmUgc3Ryb21hIGFuZCBpbW11bmUgY2VsbHMgZ2VuZXJhdGVkIGJ5IEJsdWVwcmludCAoTWFydGVucyBhbmQgU3R1bm5lbmJlcmcgMjAxMykgYW5kIEVOQ09ERSBwcm9qZWN0cyAoVGhlIEVOQ09ERSBQcm9qZWN0IENvbnNvcnRpdW0gMjAxMikuCgoKYGBge3IsIGV2YWw9RiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CiMgZGVmaW5lIHJlZmVyZW5jZSB3aXRoIGVuc2VtYmwgSURzIHRvIG1hdGNoIG91ciBJRHMKcmVmX3NlIDwtIGNlbGxkZXg6OkJsdWVwcmludEVuY29kZURhdGEoZW5zZW1ibCA9IFRSVUUpCgojIFByZWRpY3QgY2VsbCB0eXBlcywgYnJvYWRseQpwcmVkc19ibHVlX2VuYyA8LSBTaW5nbGVSOjpTaW5nbGVSKAogICMgZGF0YXNldCB3ZSB3YW50IHRvIGFubm90YXRlCiAgdGVzdCA9IHNjZSwgCiAgIyByZWZlcmVuY2UgZGF0YXNldAogIHJlZiA9IHJlZl9zZSwKICAjIGxhYmVsLm1haW4gaXMgYnJvYWQgCiAgbGFiZWxzID0gcmVmX3NlJGxhYmVsLm1haW4pCgoKIyBSZXN1bHRzIGludG8gYSB0aWJibGU6CnByZWRzX2RmIDwtIHByZWRzX2RmICU+JQogIGRwbHlyOjpiaW5kX3Jvd3MoCiAgICB0aWJibGU6OmFzX3RpYmJsZShwcmVkc19ibHVlX2VuYyRsYWJlbHMpICU+JQogICAgZHBseXI6OnJlbmFtZShjZWxsdHlwZSA9IHZhbHVlKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY2VsbF9iYXJjb2RlID0gcm93bmFtZXMocHJlZHNfYmx1ZV9lbmMpLAogICAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSAiYmx1ZXByaW50X2VuY29kZSIpCiAgKQoKIyBTYXZlIHRoZSBjZWxsdHlwZXM6CmJsdWVwcmludF9jZWxsdHlwZXMgPC0gdW5pcXVlKHJlZl9zZSRsYWJlbC5tYWluKQoKIyBBbmQgdGhlIGhlYXRtYXA6ClNpbmdsZVI6OnBsb3RTY29yZUhlYXRtYXAocHJlZHNfYmx1ZV9lbmMpCmBgYAoKCldoYXQgZGlkIHRoZSByZWZlcmVuY2VzIHByZWRpY3Q/CgoKYGBge3IsIGV2YWw9Rn0KIyBIb3cgbWFueSBvZiBlYWNoIGNlbGx0eXBlPwpwcmVkc19kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKHJlZmVyZW5jZSA9PSAiaHBjYSIpICU+JQogIGRwbHlyOjpjb3VudChjZWxsdHlwZSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLW4pCgoKcHJlZHNfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihyZWZlcmVuY2UgPT0gImJsdWVwcmludF9lbmNvZGUiKSAlPiUKICBkcGx5cjo6Y291bnQoY2VsbHR5cGUpICU+JQogIGRwbHlyOjphcnJhbmdlKC1uKQpgYGAKCldlJ2xsIGhhdmUgdG8gaGFybW9uaXplIHRoZSBjZWxsdHlwZSBuYW1lcyBiZXR3ZWVuIHJlZmVyZW5jZXMgdG8gZG8gYSByb2J1c3QgY29tcGFyaXNvbiwgYnV0IGZyb20gYSBfdmVyeV8gcXVpY2sgZ2xhbmNlLCBvdmVybGFwIGlzIHRoaW5uZXIgdGhhbiBvbmUgbWlnaHQgbGlrZS4KVGhhdCBzYWlkLCB3ZSBkb24ndCBuZWNlc3NhcmlseSBleHBlY3QgQmx1ZXByaW50L0VOQ09ERSB0byBkbyBwYXJ0aWN1bGFybHkgd2VsbCBhbnl3YXlzIQoKCkZvciBhIGNsZWFyZXIgY29tcGFyaXNvbiwgd2UnbGwgaGFybW9uaXplIHByZWRpY3RlZCBjZWxsdHlwZXMsIGJ1dCBsZXQncyBqdXN0IGZvY3VzIG9uIGV4YWN0bHkgbWF0Y2hpbmcgY2VsbHR5cGVzIGFzIGEgc3RhcnQgLSAKYGBge3IsIGV2YWw9Rn0KIyBMZXQncyBzZWUgd2hhdCB3ZSBoYXZlIGhlcmU6CnNvcnQoaHBjYV9jZWxsdHlwZXMpCnNvcnQoYmx1ZXByaW50X2NlbGx0eXBlcykKCiMgTWFudWFsbHkgY29tcGlsZWQgZGF0YSByYW1lIG9mIGNlbGx0eXBlcyByb3VnaGx5IHByZXNlbnQgaW4gQk9USCByZWZlcmVuY2VzCiMgIGZvciBub3cgbGVhdmluZyB0aGUgUHJlL1BybyBCLWNlbGxzIG91dApzaGFyZWRfY2VsbHR5cGVzX2RmIDwtIHRpYmJsZTo6dHJpYmJsZSgKICB+aHBjYV9jZWxsdHlwZSwgfmJsdWVwcmludF9jZWxsdHlwZSwKICAjIEJvdGggcmVmZXJlbmNlcyBoYXZlIGl0OgogICJBc3Ryb2N5dGUiLCAiQXN0cm9jeXRlcyIsCiAgIkJfY2VsbCIsICJCLWNlbGxzIiwKICAiQ2hvbmRyb2N5dGVzIiwiQ2hvbmRyb2N5dGVzIiwKICAiREMiLCAiREMiLCAKICAiRW5kb3RoZWxpYWxfY2VsbHMiLCAiRW5kb3RoZWxpYWwgY2VsbHMiLCAKICAiRXBpdGhlbGlhbF9jZWxscyIsICJFcGl0aGVsaWFsIGNlbGxzIiwgCiAgIkZpYnJvYmxhc3RzIiwgIkZpYnJvYmxhc3RzIiwgCiAgIktlcmF0aW5vY3l0ZXMiLCAiS2VyYXRpbm9jeXRlcyIsIAogICJNYWNyb3BoYWdlIiwgIk1hY3JvcGhhZ2VzIiwgCiAgIk1vbm9jeXRlIiwgIk1vbm9jeXRlcyIsIAogICJOZXVyb25zIiwgIk5ldXJvbnMiLCAKICAiTmV1dHJvcGhpbHMiLCAiTmV1dHJvcGhpbHMiLCAKICAiTktfY2VsbCIsICJOSyBjZWxscyIsIAogICJTbW9vdGhfbXVzY2xlX2NlbGxzIiwgIlNtb290aCBtdXNjbGUiLCAKICAjIHR3byBncm91cHMgLSBjb2xsYXBzZSBiYWNrIHRvIG92ZXJhbGwgVCBjZWxscwogICJUX2NlbGxzIiwgIkNEOCsgVC1jZWxscyIsCiAgIlRfY2VsbHMiLCAiQ0Q0KyBULWNlbGxzIiwKICAjIHR3byBncm91cHMgb2YgSFNDcywgYWdhaW4sIGNvbGxhcHNlIGJhY2sgdG8gb3ZlcmFsbAogICJIU0NfLUctQ1NGIiwgIkhTQyIsIAogICJIU0NfQ0QzNCsiLCAiSFNDIgopICU+JQogIGRwbHlyOjptdXRhdGUoaGFybW9uaXplZF9jZWxsdHlwZSA9IGlmZWxzZShibHVlcHJpbnRfY2VsbHR5cGUgPT0gIkhTQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsdWVwcmludF9jZWxsdHlwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhwY2FfY2VsbHR5cGUpKQoKaGFybW9uaXplX2NlbGx0eXBlcyA8LSBmdW5jdGlvbihwcmVkc19kZiwgcmVmZXJlbmNlX25hbWUpIHsKICBpZiAocmVmZXJlbmNlX25hbWUgPT0gImhwY2EiKSB7CiAgICBzaGFyZWRfY2VsbHR5cGVzX2NvbG5hbWUgPC0gcmxhbmc6OnN5bSgiaHBjYV9jZWxsdHlwZSIpCiAgfSBlbHNlIHsKICAgIHNoYXJlZF9jZWxsdHlwZXNfY29sbmFtZSA8LSBybGFuZzo6c3ltKCJibHVlcHJpbnRfY2VsbHR5cGUiKQogIH0KICAKICBmaWx0ZXJlZF9wcmVkc19kZiA8LSBwcmVkc19kZiAlPiUKICAgIGRwbHlyOjpmaWx0ZXIocmVmZXJlbmNlID09IHJlZmVyZW5jZV9uYW1lKSAKICAKICBzeW1fcmVmZXJlbmNlX25hbWUgPC0gcmxhbmc6OnN5bShyZWZlcmVuY2VfbmFtZSkKICBmaWx0ZXJlZF9wcmVkc19kZiAlPiUKICAgIGRwbHlyOjppbm5lcl9qb2luKAogICAgICBkcGx5cjo6c2VsZWN0KAogICAgICAgIHNoYXJlZF9jZWxsdHlwZXNfZGYsIAogICAgICAgIGNlbGx0eXBlID0ge3tzaGFyZWRfY2VsbHR5cGVzX2NvbG5hbWV9fSwKICAgICAgICBoYXJtb25pemVkX2NlbGx0eXBlCiAgICAgICkKICAgICkgJT4lIAogICAgZHBseXI6OnNlbGVjdChjZWxsX2JhcmNvZGUsIHt7c3ltX3JlZmVyZW5jZV9uYW1lfX0gOj0gaGFybW9uaXplZF9jZWxsdHlwZSkgJT4lCiAgICBkcGx5cjo6cmlnaHRfam9pbihmaWx0ZXJlZF9wcmVkc19kZikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKHt7cmVmZXJlbmNlX25hbWV9fSA6PSBkcGx5cjo6aWZfZWxzZSgKICAgICAge3tzeW1fcmVmZXJlbmNlX25hbWV9fSAlaW4lIHNoYXJlZF9jZWxsdHlwZXNfZGYkaGFybW9uaXplZF9jZWxsdHlwZSwKICAgICAge3tzeW1fcmVmZXJlbmNlX25hbWV9fSwKICAgICAgY2VsbHR5cGUKICAgICkpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoLWNlbGx0eXBlLCAtcmVmZXJlbmNlKQogIH0KICAKcHJlZHNfZGZfaGFybW9uaXplZCA8LSBoYXJtb25pemVfY2VsbHR5cGVzKHByZWRzX2RmLCAiYmx1ZXByaW50X2VuY29kZSIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oCiAgICBoYXJtb25pemVfY2VsbHR5cGVzKHByZWRzX2RmLCAiaHBjYSIpICAKICApCgojIEhvdyBvZnRlbiBkbyB0aGV5IG1hdGNoPwoKcHJlZHNfZGZfaGFybW9uaXplZCAlPiUKICBkcGx5cjo6c3VtbWFyaXplKHBlcmNlbnRfc2FtZSA9IHN1bShibHVlcHJpbnRfZW5jb2RlID09IGhwY2EpIC8gZHBseXI6Om4oKSApCgoKIyBXaGVyZSBkbyB0aGV5IG5vdCBtYXRjaD8KcHJlZHNfZGZfaGFybW9uaXplZCAlPiUKICBkcGx5cjo6ZmlsdGVyKGhwY2EgIT0gYmx1ZXByaW50X2VuY29kZSkKICAKIyBBcmUgdGhlcmUgc3BlY2lmaWMgY29tYmluYXRpb25zIHRoYXQgZ2V0IGRpZmZlcmVudGx5IGFubm90YXRlZD8KcHJlZHNfZGZfaGFybW9uaXplZCAlPiUKICBkcGx5cjo6ZmlsdGVyKGhwY2EgIT0gYmx1ZXByaW50X2VuY29kZSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtY2VsbF9iYXJjb2RlKSAlPiUKICBkcGx5cjo6Y291bnQoYmx1ZXByaW50X2VuY29kZSwgaHBjYSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLW4pCgpgYGAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbl9pbmZvLCBldmFsPVRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCg==